package com.lighting.shortlink.project.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.lighting.shortlink.project.dao.entity.*;
import com.lighting.shortlink.project.dao.mapper.*;
import com.lighting.shortlink.project.dto.req.ShortLinkGroupStatsAccessRecordReqDTO;
import com.lighting.shortlink.project.dto.req.ShortLinkGroupStatsReqDTO;
import com.lighting.shortlink.project.dto.req.ShortLinkStatsAccessRecordReqDTO;
import com.lighting.shortlink.project.dto.req.ShortLinkStatsReqDTO;
import com.lighting.shortlink.project.dto.resp.*;
import com.lighting.shortlink.project.service.ShortLinkStatsService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author : lighting
 */
@Service
@RequiredArgsConstructor
public class ShortLinkStatsServiceImpl implements ShortLinkStatsService {

    private final LinkAccessStatsMapper linkAccessStatsMapper;// 基础访问监控数据
    private final LinkLocaleStatsMapper linkLocaleStatsMapper;//位置
    private final LinkAccessLogsMapper linkAccessLogsMapper;// 访问日志
    private final LinkBrowserStatsMapper linkBrowserStatsMapper;// 浏览器
    private final LinkOsStatsMapper linkOsStatsMapper;// 操作系统
    private final LinkDeviceStatsMapper linkDeviceStatsMapper;// 设备
    private final LinkNetworkStatsMapper linkNetworkStatsMapper;// 网络

    /**
     * 获取单个短链接监控数据
     *
     * @param requestParam 获取短链接监控数据入参
     * @return 短链接监控数据
     */
    @Override
    public ShortLinkStatsRespDTO oneShortLinkStats(ShortLinkStatsReqDTO requestParam) {
//       LinkAccessStatsDO 链接访问统计数据的汇总数据对象，包含 id、完整短链接、分组标识、日期、访问量、独立访客数、独立 IP 数、小时、星期 等属性。
//       获取指定短链接的访问统计数据列表
        List<LinkAccessStatsDO> listStatsByShortLink = linkAccessStatsMapper.listStatsByShortLink(requestParam);
//      如果统计数据列表为空，则返回空值
        if (CollUtil.isEmpty(listStatsByShortLink)) {
            return null;
        }

//      ShortLinkStatsAccessDailyRespDTO 是表示每日访问统计数据的数据传输对象，包含日期、访问量、独立访客数和独立 IP 数等属性。
//      获取指定短链接在日期范围内的 每日访问统计数据列表
        List<ShortLinkStatsAccessDailyRespDTO> daily = new ArrayList<>();
//        DateUtil.rangeToList方法生成一个日期范围列表(把区间内的所有 天 都存在 list 中)，这里指定的日期字段是 DateField.DAY_OF_MONTH，表示按天生成日期范围
        List<String> rangeDates = DateUtil.rangeToList(
                        DateUtil.parse(requestParam.getStartDate()),
                        DateUtil.parse(requestParam.getEndDate()),
                        DateField.DAY_OF_MONTH).stream()
//                 对流中的每个日期对象应用DateUtil.formatDate方法，该方法将日期对象格式化为字符串，方便下面统一格式匹配
                .map(DateUtil::formatDate)
                .toList();// 将流中的字符串收集到一个新的列表中

        rangeDates.forEach(eachDate -> listStatsByShortLink.stream()//遍历日期范围
                .filter(item -> Objects.equals(eachDate, DateUtil.formatDate(item.getDate())))//筛选出与当前日期相等的数据
                .findFirst()// 因为有唯一索引保证只有一个，或者没有
                // 如果找到数据，则构建访问统计的 DTO
                .ifPresentOrElse(item -> {
                            // 构建每日访问统计 DTO
                            ShortLinkStatsAccessDailyRespDTO accessDailyRespDTO = ShortLinkStatsAccessDailyRespDTO.builder()
                                    .date(eachDate)
                                    .pv(item.getPv())
                                    .uv(item.getUv())
                                    .uip(item.getUip())
                                    .build();
                            daily.add(accessDailyRespDTO);
                        },
                        // 如果没有找到数据，则构建空数据的每日访问统计DTO
                        () -> {
                            ShortLinkStatsAccessDailyRespDTO accessDailyRespDTO = ShortLinkStatsAccessDailyRespDTO.builder()
                                    .date(eachDate)
                                    .pv(0)
                                    .uv(0)
                                    .uip(0)
                                    .build();
                            daily.add(accessDailyRespDTO);
                        }));

//      获取指定短链接的国内地区访问统计数据列表
        List<ShortLinkStatsLocaleCNRespDTO> localeCnStats = new ArrayList<>();
//      从数据库中查询指定短链接的国内地区访问统计数据
        List<LinkLocaleStatsDO> listedLocaleByShortLink = linkLocaleStatsMapper.listLocaleByShortLink(requestParam);
        // 计算国内地区访问统计数据的总和
        int localeCnSum = listedLocaleByShortLink.stream()
                .mapToInt(LinkLocaleStatsDO::getCnt)
                .sum();
        // 遍历查询结果，为每个地区构建访问统计DTO
        listedLocaleByShortLink.forEach(eachLocale -> {
//            计算地区访问比例
            double ratio = (double) eachLocale.getCnt() / localeCnSum;
            double actualRatio = Math.round(ratio * 100) / 100.0;//Math.round()四舍五入取整数，/100 保留两位小数
            // 构建国内地区访问统计DTO
            ShortLinkStatsLocaleCNRespDTO localeCNRespDTO = ShortLinkStatsLocaleCNRespDTO.builder()
                    .cnt(eachLocale.getCnt())
                    .locale(eachLocale.getProvince())
                    .ratio(actualRatio)
                    .build();
            localeCnStats.add(localeCNRespDTO);
        });

//      获取指定短链接的一天的 每小时访问统计数据列表
        List<Integer> hourStats = new ArrayList<>();
        List<LinkAccessStatsDO> listHourStatsByShortLink = linkAccessStatsMapper.listHourStatsByShortLink(requestParam);
        for (int h = 0; h < 24; h++) {
            AtomicInteger hour = new AtomicInteger(h);
            Integer hourCnt = listHourStatsByShortLink.stream()
                    .filter(eacHour -> Objects.equals(eacHour.getHour(), hour.get()))
                    .findFirst()//获取当前小时的数据
                    .map(LinkAccessStatsDO::getPv)// 从每个 LinkAccessStatsDO获取当前小时的 PV 访问量
                    .orElse(0);//如果当前小时没有数据，默认为 0
            hourStats.add(hourCnt);
        }

// 创建一个空列表，用于存储高频访问IP统计DTO对象
        List<ShortLinkStatsTopIpRespDTO> topIpStats = new ArrayList<>();
        // 从数据库中查询指定短链接的高频访问IP统计数据，并将结果存储在listTopIpByShortLink列表中
        List<HashMap<String, Object>> listTopIpByShortLink = linkAccessLogsMapper.listTopIpByShortLink(requestParam);
        // 遍历查询结果，为每个IP构建访问统计DTO
        listTopIpByShortLink.forEach(eachTopIp -> {
            // 构建高频访问IP统计DTO
            ShortLinkStatsTopIpRespDTO statsTopIpRespDTO = ShortLinkStatsTopIpRespDTO.builder()
                    .ip(eachTopIp.get("ip").toString()) // 从each中获取IP地址字符串
                    .cnt(Integer.parseInt(eachTopIp.get("count").toString()))
                    .build();
            topIpStats.add(statsTopIpRespDTO);
        });

// 获取指定短链接的每周访问统计数据列表
        List<Integer> weekdayStats = new ArrayList<>();
        List<LinkAccessStatsDO> listWeekdayStatsByShortLink = linkAccessStatsMapper.listWeekdayStatsByShortLink(requestParam);
        for (int i = 1; i < 8; i++) {
            AtomicInteger weekday = new AtomicInteger(i);
            int weekdayCnt = listWeekdayStatsByShortLink.stream()
                    .filter(each -> Objects.equals(each.getWeekday(), weekday.get()))
                    .findFirst()//因为有唯一索引保证只有一个，或者没有
                    .map(LinkAccessStatsDO::getPv)//从 LinkAccessStatsDO 中提取 PV 访问量
                    .orElse(0);
            weekdayStats.add(weekdayCnt);
        }

// 获取指定短链接的浏览器访问统计数据列表
        List<ShortLinkStatsBrowserRespDTO> browserStats = new ArrayList<>();
        List<HashMap<String, Object>> listBrowserStatsByShortLink = linkBrowserStatsMapper.listBrowserStatsByShortLink(requestParam);
        int browserSum = listBrowserStatsByShortLink.stream()//
                // 从每个 HashMap 对象中提取键为 count 的值，并将其转换为 int 类型。
                .mapToInt(each -> Integer.parseInt(each.get("count").toString()))
                .sum();//累加求和并返回
        listBrowserStatsByShortLink.forEach(each -> {
            // 计算浏览器访问比例
            double ratio = (double) Integer.parseInt(each.get("count").toString()) / browserSum;
            double actualRatio = Math.round(ratio * 100.0) / 100.0;
            // 构建浏览器访问统计DTO
            ShortLinkStatsBrowserRespDTO browserRespDTO = ShortLinkStatsBrowserRespDTO.builder()
                    .cnt(Integer.parseInt(each.get("count").toString()))
                    .browser(each.get("browser").toString())
                    .ratio(actualRatio)
                    .build();
            browserStats.add(browserRespDTO);
        });

// 获取指定短链接的操作系统访问统计数据列表
        List<ShortLinkStatsOsRespDTO> osStats = new ArrayList<>();
        List<HashMap<String, Object>> listOsStatsByShortLink = linkOsStatsMapper.listOsStatsByShortLink(requestParam);
        int osSum = listOsStatsByShortLink.stream()
                .mapToInt(each -> Integer.parseInt(each.get("count").toString()))
                .sum();
        listOsStatsByShortLink.forEach(each -> {
            // 计算操作系统访问比例
            double ratio = (double) Integer.parseInt(each.get("count").toString()) / osSum;
            double actualRatio = Math.round(ratio * 100.0) / 100.0;
            // 构建操作系统访问统计DTO
            ShortLinkStatsOsRespDTO osRespDTO = ShortLinkStatsOsRespDTO.builder()
                    .cnt(Integer.parseInt(each.get("count").toString()))
                    .os(each.get("os").toString())
                    .ratio(actualRatio)
                    .build();
            osStats.add(osRespDTO);
        });

// 获取指定短链接的访客类型统计数据列表
        List<ShortLinkStatsUvRespDTO> uvTypeStats = new ArrayList<>();
//       <oldUserCnt,cnt> <newUserCnt,cnt>
        HashMap<String, Object> findUvTypeByShortLink = linkAccessLogsMapper.findUvTypeCntByShortLink(requestParam);
        int oldUserCnt = Integer.parseInt(
//       如果 findUvTypeByShortLink 不为 null，则创建一个包含该对象的 Optional；如果为 null，则创建一个空的 Optional
                Optional.ofNullable(findUvTypeByShortLink)
                        .map(each -> each.get("oldUserCnt"))
                        .map(Object::toString)
                        .orElse("0")
        );
        int newUserCnt = Integer.parseInt(
                Optional.ofNullable(findUvTypeByShortLink)
                        .map(each -> each.get("newUserCnt"))
                        .map(Object::toString)
                        .orElse("0")
        );
        int uvSum = oldUserCnt + newUserCnt;//总的用户访问量
        double oldRatio = (double) oldUserCnt / uvSum;
        double actualOldRatio = Math.round(oldRatio * 100.0) / 100.0;
        double newRatio = (double) newUserCnt / uvSum;
        double actualNewRatio = Math.round(newRatio * 100.0) / 100.0;
// 构建新用户访问统计DTO
        ShortLinkStatsUvRespDTO newUvRespDTO = ShortLinkStatsUvRespDTO.builder()
                .uvType("newUser")
                .cnt(newUserCnt)
                .ratio(actualNewRatio)
                .build();
        uvTypeStats.add(newUvRespDTO);
// 构建老用户访问统计DTO
        ShortLinkStatsUvRespDTO oldUvRespDTO = ShortLinkStatsUvRespDTO.builder()
                .uvType("oldUser")
                .cnt(oldUserCnt)
                .ratio(actualOldRatio)
                .build();
        uvTypeStats.add(oldUvRespDTO);

// 获取指定短链接的访问设备类型统计数据列表
        List<ShortLinkStatsDeviceRespDTO> deviceStats = new ArrayList<>();
        List<LinkDeviceStatsDO> listDeviceStatsByShortLink = linkDeviceStatsMapper.listDeviceStatsByShortLink(requestParam);
        int deviceSum = listDeviceStatsByShortLink.stream()
                .mapToInt(LinkDeviceStatsDO::getCnt)
                .sum();
        listDeviceStatsByShortLink.forEach(each -> {
            // 计算访问设备类型比例
            double ratio = (double) each.getCnt() / deviceSum;
            double actualRatio = Math.round(ratio * 100.0) / 100.0;
            // 构建访问设备类型统计DTO
            ShortLinkStatsDeviceRespDTO deviceRespDTO = ShortLinkStatsDeviceRespDTO.builder()
                    .cnt(each.getCnt())
                    .device(each.getDevice())
                    .ratio(actualRatio)
                    .build();
            deviceStats.add(deviceRespDTO);
        });

// 获取指定短链接的访问网络类型统计数据列表
        List<ShortLinkStatsNetworkRespDTO> networkStats = new ArrayList<>();
        List<LinkNetworkStatsDO> listNetworkStatsByShortLink = linkNetworkStatsMapper.listNetworkStatsByShortLink(requestParam);
        int networkSum = listNetworkStatsByShortLink.stream()
                .mapToInt(LinkNetworkStatsDO::getCnt)
                .sum();
        listNetworkStatsByShortLink.forEach(each -> {
            // 计算访问网络类型比例
            double ratio = (double) each.getCnt() / networkSum;
            double actualRatio = Math.round(ratio * 100.0) / 100.0;
            // 构建访问网络类型统计DTO
            ShortLinkStatsNetworkRespDTO networkRespDTO = ShortLinkStatsNetworkRespDTO.builder()
                    .cnt(each.getCnt())
                    .network(each.getNetwork())
                    .ratio(actualRatio)
                    .build();
            networkStats.add(networkRespDTO);
        });
//      LinkAccessStatsDO 是表示链接访问统计数据的具体单个数据对象，包含 id、完整短链接、分组标识、日期、访问量、独立访客数、独立 IP 数、小时和星期等属性。
//      获取指定短链接的 PV、UV、UID 的 数量
        LinkAccessStatsDO pvUvUidStatsByShortLink = linkAccessLogsMapper.findPvUvUidStatsByShortLink(requestParam);
// 构建短链接统计响应DTO
        return ShortLinkStatsRespDTO.builder()
                // pv: 页面浏览量，即访问短链接的总次数
                .pv(pvUvUidStatsByShortLink.getPv())
                // uv: 唯一访客数，即访问短链接的不同用户数
                .uv(pvUvUidStatsByShortLink.getUv())
                // uip: 唯一IP数，即访问短链接的不同IP地址数
                .uip(pvUvUidStatsByShortLink.getUip())
                // daily: 每日访问统计列表，包含每天的访问数据
                .daily(daily)
                // localeCnStats: 国内地区访问统计列表，包含国内各地区的访问数据
                .localeCnStats(localeCnStats)
                // hourStats: 每小时访问统计列表，包含每小时的访问数据
                .hourStats(hourStats)
                // topIpStats: 高频访问IP统计列表，包含访问次数最多的IP地址数据
                .topIpStats(topIpStats)
                // weekdayStats: 每日访问统计列表，包含一周中每天的访问数据
                .weekdayStats(weekdayStats)
                // browserStats: 浏览器访问统计列表，包含不同浏览器的访问数据
                .browserStats(browserStats)
                // osStats: 操作系统访问统计列表，包含不同操作系统的访问数据
                .osStats(osStats)
                // uvTypeStats: 访客类型统计列表，包含新访客和老访客的访问数据
                .uvTypeStats(uvTypeStats)
                // deviceStats: 设备类型访问统计列表，包含不同设备类型的访问数据
                .deviceStats(deviceStats)
                // networkStats: 网络类型访问统计列表，包含不同网络类型的访问数据
                .networkStats(networkStats)
                .build();
    }

    /**
     * 获取分组短链接监控数据
     *
     * @param requestParam 获取分组短链接监控数据入参
     * @return 分组短链接监控数据
     */
    @Override
    public ShortLinkStatsRespDTO groupShortLinkStats(ShortLinkGroupStatsReqDTO requestParam) {
//        查分组的短链接监控信息
        List<LinkAccessStatsDO> listStatsByGroup = linkAccessStatsMapper.listStatsByGroup(requestParam);
        if (CollUtil.isEmpty(listStatsByGroup)) {
            return null;
        }

        // 基础访问详情（同上）
        List<ShortLinkStatsAccessDailyRespDTO> daily = new ArrayList<>();
        List<String> rangeDates = DateUtil.rangeToList(DateUtil.parse(requestParam.getStartDate()), DateUtil.parse(requestParam.getEndDate()), DateField.DAY_OF_MONTH).stream()
                .map(DateUtil::formatDate)
                .toList();
        rangeDates.forEach(each -> listStatsByGroup.stream()
                .filter(item -> Objects.equals(each, DateUtil.formatDate(item.getDate())))
                .findFirst()
                .ifPresentOrElse(item -> {
                    ShortLinkStatsAccessDailyRespDTO accessDailyRespDTO = ShortLinkStatsAccessDailyRespDTO.builder()
                            .date(each)
                            .pv(item.getPv())
                            .uv(item.getUv())
                            .uip(item.getUip())
                            .build();
                    daily.add(accessDailyRespDTO);
                }, () -> {
                    ShortLinkStatsAccessDailyRespDTO accessDailyRespDTO = ShortLinkStatsAccessDailyRespDTO.builder()
                            .date(each)
                            .pv(0)
                            .uv(0)
                            .uip(0)
                            .build();
                    daily.add(accessDailyRespDTO);
                }));
        // 地区访问详情（仅国内）
        List<ShortLinkStatsLocaleCNRespDTO> localeCnStats = new ArrayList<>();
        List<LinkLocaleStatsDO> listedLocaleByGroup = linkLocaleStatsMapper.listLocaleByGroup(requestParam);
        int localeCnSum = listedLocaleByGroup.stream()
                .mapToInt(LinkLocaleStatsDO::getCnt)
                .sum();
        listedLocaleByGroup.forEach(each -> {
            double ratio = (double) each.getCnt() / localeCnSum;
            double actualRatio = Math.round(ratio * 100.0) / 100.0;
            ShortLinkStatsLocaleCNRespDTO localeCNRespDTO = ShortLinkStatsLocaleCNRespDTO.builder()
                    .cnt(each.getCnt())
                    .locale(each.getProvince())
                    .ratio(actualRatio)
                    .build();
            localeCnStats.add(localeCNRespDTO);
        });
        // 小时访问详情
        List<Integer> hourStats = new ArrayList<>();
        List<LinkAccessStatsDO> listHourStatsByGroup = linkAccessStatsMapper.listHourStatsByGroup(requestParam);
        for (int i = 0; i < 24; i++) {
            AtomicInteger hour = new AtomicInteger(i);
//            Integer hourInteger = Integer.valueOf(i);
            int hourCnt = listHourStatsByGroup.stream()
                    .filter(each -> Objects.equals(each.getHour(), hour.get()))
//                    .filter(each -> Objects.equals(each.getHour(),hourInteger))
                    .findFirst()
                    .map(LinkAccessStatsDO::getPv)
                    .orElse(0);
            hourStats.add(hourCnt);
        }
        // 高频访问IP详情
        List<ShortLinkStatsTopIpRespDTO> topIpStats = new ArrayList<>();
        List<HashMap<String, Object>> listTopIpByGroup = linkAccessLogsMapper.listTopIpByGroup(requestParam);
        listTopIpByGroup.forEach(each -> {
            ShortLinkStatsTopIpRespDTO statsTopIpRespDTO = ShortLinkStatsTopIpRespDTO.builder()
                    .ip(each.get("ip").toString())
                    .cnt(Integer.parseInt(each.get("count").toString()))
                    .build();
            topIpStats.add(statsTopIpRespDTO);
        });
        // 一周访问详情
        List<Integer> weekdayStats = new ArrayList<>();
        List<LinkAccessStatsDO> listWeekdayStatsByGroup = linkAccessStatsMapper.listWeekdayStatsByGroup(requestParam);
        for (int i = 1; i < 8; i++) {
            AtomicInteger weekday = new AtomicInteger(i);
            int weekdayCnt = listWeekdayStatsByGroup.stream()
                    .filter(each -> Objects.equals(each.getWeekday(), weekday.get()))
                    .findFirst()
                    .map(LinkAccessStatsDO::getPv)
                    .orElse(0);
            weekdayStats.add(weekdayCnt);
        }
        // 浏览器访问详情
        List<ShortLinkStatsBrowserRespDTO> browserStats = new ArrayList<>();
        List<HashMap<String, Object>> listBrowserStatsByGroup = linkBrowserStatsMapper.listBrowserStatsByGroup(requestParam);
        int browserSum = listBrowserStatsByGroup.stream()
                .mapToInt(each -> Integer.parseInt(each.get("count").toString()))
                .sum();
        listBrowserStatsByGroup.forEach(each -> {
            double ratio = (double) Integer.parseInt(each.get("count").toString()) / browserSum;
            double actualRatio = Math.round(ratio * 100.0) / 100.0;
            ShortLinkStatsBrowserRespDTO browserRespDTO = ShortLinkStatsBrowserRespDTO.builder()
                    .cnt(Integer.parseInt(each.get("count").toString()))
                    .browser(each.get("browser").toString())
                    .ratio(actualRatio)
                    .build();
            browserStats.add(browserRespDTO);
        });
        // 操作系统访问详情
        List<ShortLinkStatsOsRespDTO> osStats = new ArrayList<>();
        List<HashMap<String, Object>> listOsStatsByGroup = linkOsStatsMapper.listOsStatsByGroup(requestParam);
        int osSum = listOsStatsByGroup.stream()
                .mapToInt(each -> Integer.parseInt(each.get("count").toString()))
                .sum();
        listOsStatsByGroup.forEach(each -> {
            double ratio = (double) Integer.parseInt(each.get("count").toString()) / osSum;
            double actualRatio = Math.round(ratio * 100.0) / 100.0;
            ShortLinkStatsOsRespDTO osRespDTO = ShortLinkStatsOsRespDTO.builder()
                    .cnt(Integer.parseInt(each.get("count").toString()))
                    .os(each.get("os").toString())
                    .ratio(actualRatio)
                    .build();
            osStats.add(osRespDTO);
        });
        // 访问设备类型详情
        List<ShortLinkStatsDeviceRespDTO> deviceStats = new ArrayList<>();
        List<LinkDeviceStatsDO> listDeviceStatsByGroup = linkDeviceStatsMapper.listDeviceStatsByGroup(requestParam);
        int deviceSum = listDeviceStatsByGroup.stream()
                .mapToInt(LinkDeviceStatsDO::getCnt)
                .sum();
        listDeviceStatsByGroup.forEach(each -> {
            double ratio = (double) each.getCnt() / deviceSum;
            double actualRatio = Math.round(ratio * 100.0) / 100.0;
            ShortLinkStatsDeviceRespDTO deviceRespDTO = ShortLinkStatsDeviceRespDTO.builder()
                    .cnt(each.getCnt())
                    .device(each.getDevice())
                    .ratio(actualRatio)
                    .build();
            deviceStats.add(deviceRespDTO);
        });
        // 访问网络类型详情
        List<ShortLinkStatsNetworkRespDTO> networkStats = new ArrayList<>();
        List<LinkNetworkStatsDO> listNetworkStatsByGroup = linkNetworkStatsMapper.listNetworkStatsByGroup(requestParam);
        int networkSum = listNetworkStatsByGroup.stream()
                .mapToInt(LinkNetworkStatsDO::getCnt)
                .sum();
        listNetworkStatsByGroup.forEach(each -> {
            double ratio = (double) each.getCnt() / networkSum;
            double actualRatio = Math.round(ratio * 100.0) / 100.0;
            ShortLinkStatsNetworkRespDTO networkRespDTO = ShortLinkStatsNetworkRespDTO.builder()
                    .cnt(each.getCnt())
                    .network(each.getNetwork())
                    .ratio(actualRatio)
                    .build();
            networkStats.add(networkRespDTO);
        });
        // 基础访问数据
        LinkAccessStatsDO pvUvUidStatsByGroup = linkAccessLogsMapper.findPvUvUidStatsByGroup(requestParam);

        return ShortLinkStatsRespDTO.builder()
                .pv(pvUvUidStatsByGroup.getPv())
                .uv(pvUvUidStatsByGroup.getUv())
                .uip(pvUvUidStatsByGroup.getUip())
                .daily(daily)
                .localeCnStats(localeCnStats)
                .hourStats(hourStats)
                .topIpStats(topIpStats)
                .weekdayStats(weekdayStats)
                .browserStats(browserStats)
                .osStats(osStats)
                .deviceStats(deviceStats)
                .networkStats(networkStats)
                .build();
    }

    /**
     * 访问单个短链接指定时间内访问记录监控数据
     *
     * @param requestParam 获取短链接监控访问记录数据入参 包含短链接ID、删除标志、创建时间范围等信息。
     * @return 访问记录监控数据
     */
    @Override
    public IPage<ShortLinkStatsAccessRecordRespDTO> shortLinkStatsAccessRecord(ShortLinkStatsAccessRecordReqDTO requestParam) {
        // 构建查询条件
        LambdaQueryWrapper<LinkAccessLogsDO> queryWrapper = Wrappers.lambdaQuery(LinkAccessLogsDO.class)
                .eq(LinkAccessLogsDO::getGid, requestParam.getGid()) // 筛选短链接ID匹配的记录
                .eq(LinkAccessLogsDO::getFullShortUrl, requestParam.getFullShortUrl()) // 筛选短链接URL匹配的记录
                .eq(LinkAccessLogsDO::getDelFlag, 0) // 筛选未删除的记录
                .between(LinkAccessLogsDO::getCreateTime, requestParam.getStartDate(), requestParam.getEndDate()) // 筛选创建时间在指定范围内的记录
                .orderByDesc(LinkAccessLogsDO::getCreateTime); // 按创建时间降序排列

        // 执行查询，获取分页的访问记录
        IPage<LinkAccessLogsDO> linkAccessLogsDOIPage = linkAccessLogsMapper.selectPage(requestParam, queryWrapper);//page,Wrapper

        // 将查询结果转换为 ShortLinkStatsAccessRecordRespDTO 短链接监控日志记录数据
        IPage<ShortLinkStatsAccessRecordRespDTO> actualResult = linkAccessLogsDOIPage.convert(each ->
                BeanUtil.toBean(each, ShortLinkStatsAccessRecordRespDTO.class));

        // 获取所有用户的访问记录
        List<String> userAccessLogsList = actualResult.getRecords().stream()
                .map(ShortLinkStatsAccessRecordRespDTO::getUser) // 提取每个访问记录的用户信息
                .toList();

        // 查询每个用户的访问类型统计信息
        List<Map<String, Object>> uvTypeList = linkAccessLogsMapper.selectUvTypeByUsers(
                requestParam.getGid(), // 短链接ID
                requestParam.getFullShortUrl(), // 短链接URL
                requestParam.getStartDate(), // 开始日期
                requestParam.getEndDate(), // 结束日期
                userAccessLogsList // 用户访问记录列表
        );

        // 为每个访问记录设置访问类型
        actualResult.getRecords().forEach(each -> {
            String uvType = uvTypeList.stream() // 遍历用户访问类型统计信息
                    .filter(item -> Objects.equals(each.getUser(), item.get("user"))) // 筛选匹配当前访问记录用户的信息
                    .findFirst() // 获取第一个匹配项
                    .map(item -> item.get("uvType")) // 提取访问类型
                    .map(Objects::toString) // 转换为字符串
                    .orElse("旧访客"); // 如果没有找到匹配项，设置默认值为"旧访客"
            each.setUvType(uvType); // 设置访问记录的访问类型
        });

        // 返回分页结果
        return actualResult;
    }

    /**
     * 访问分组短链接指定时间内访问记录监控数据
     *
     * @param requestParam 获取分组短链接监控访问记录数据入参
     * @return 分组访问记录监控数据
     */
    @Override
    public IPage<ShortLinkStatsAccessRecordRespDTO> groupShortLinkStatsAccessRecord(ShortLinkGroupStatsAccessRecordReqDTO requestParam) {
        // 构建查询条件
        LambdaQueryWrapper<LinkAccessLogsDO> queryWrapper = Wrappers.lambdaQuery(LinkAccessLogsDO.class)
                .eq(LinkAccessLogsDO::getGid, requestParam.getGid()) // 筛选短链接ID匹配的记录
                .between(LinkAccessLogsDO::getCreateTime, requestParam.getStartDate(), requestParam.getEndDate()) // 筛选创建时间在指定范围内的记录
                .eq(LinkAccessLogsDO::getDelFlag, 0) // 筛选未删除的记录
                .orderByDesc(LinkAccessLogsDO::getCreateTime); // 按创建时间降序排列

        // 执行查询，获取分页的访问记录
        IPage<LinkAccessLogsDO> linkAccessLogsDOIPage = linkAccessLogsMapper.selectPage(requestParam, queryWrapper);

        // 将查询结果转换为 ShortLinkStatsAccessRecordRespDTO对象
        IPage<ShortLinkStatsAccessRecordRespDTO> actualResult = linkAccessLogsDOIPage.convert(each -> BeanUtil.toBean(each, ShortLinkStatsAccessRecordRespDTO.class));

        // 获取所有用户的访问记录
        List<String> userAccessLogsList = actualResult.getRecords().stream()
                .map(ShortLinkStatsAccessRecordRespDTO::getUser) // 提取每个访问记录的用户信息
                .toList();

        // 查询每个用户的访问类型统计信息
        List<Map<String, Object>> uvTypeList = linkAccessLogsMapper.selectGroupUvTypeByUsers(
                requestParam.getGid(), // 短链接ID
                requestParam.getStartDate(), // 开始日期
                requestParam.getEndDate(), // 结束日期
                userAccessLogsList // 用户访问记录列表
        );

        // 为每个访问记录设置访问类型
        actualResult.getRecords().forEach(each -> {
            String uvType = uvTypeList.stream() // 遍历用户访问类型统计信息
                    .filter(item -> Objects.equals(each.getUser(), item.get("user"))) // 筛选匹配当前访问记录用户的信息
                    .findFirst() // 获取第一个匹配项
                    .map(item -> item.get("uvType")) // 提取访问类型
                    .map(Object::toString) // 转换为字符串
                    .orElse("旧访客"); // 如果上面的 map为空，设置默认值为"旧访客"
            each.setUvType(uvType); // 设置访问记录的访问类型
        });

        // 返回分页结果
        return actualResult;
    }
}
